package io.github.annyylxyms.gynregistry.cluster;

import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class Election {

    public void electLeader(List<Server> servers) {
        //选出活着的觉得自己是主的
        List<Server> masters = servers.stream().filter(Server::isStatus).filter(server -> server.isLeader()).collect(Collectors.toList());
        if (masters.isEmpty()) {
            log.warn(" ====> [ELECT] elect for no leader:" + servers);
            elect(servers);
        } else if (masters.size() > 1) {
            log.warn(" ====> [ELECT]多主选举 elect for more than on leader:" + servers);
            elect(servers);
        } else {
            log.debug(" =====> no need to election for leader:" + masters.get(0));
        }
    }

    /**
     * 选主办法：
     * 1、各个节点自己选，保证大家选的是同一个
     * 2、外部有一个分布式锁，谁拿到锁，谁是主
     * 3、分布式一致性算法，比如paxos、raft，很复杂
     */
    private void elect(List<Server> servers) {
        Server candidate = null;
        for (Server server : servers) {
            //这里先设置所有节点都为非主节点，是为了存在多个主节点时可以重新选一个新的主节点出来（比如两台服务器同时启动，他们首次启动过程中都没有监测到有主节点，所以会把自己判为主节点，这就出现了有多个主的情况）
            server.setLeader(false);
            if (server.isStatus()) {
                if (candidate == null) {
                    candidate = server;
                } else {
                    //Server的哈希码就是url的哈希码，在类里注解定义了,直接对比url的哈希值，取最大的未为主节点
                    if (server.hashCode() > candidate.hashCode()) {
                        candidate = server;
                    }
                }
            }
        }
        if (candidate != null) {
            candidate.setLeader(true);
            log.info(" =====> elect for leader:" + candidate);
        } else {
            log.info(" =====> elect for leader failed:" + servers);
        }
    }
}
